import test from "ava"
import { isObject, validateAndMerge, mergeHeaders, deepMerge } from "../src/utils/merge"
import { normalizeRequestMethod, normalizeRetryOptions } from "../src/utils/normalize"
import { findUnknownOptions } from "../src/utils/options"
import delay from "../src/utils/delay"

/**
 因为我们在测试泛型函数，不需要 TypeScript 严格的类型检查
 
 所以这里使用 any 类型来避免类型错误
 */

test('工具函数 - isObject 基本判断', (t) => {
    // 正确识别对象
    t.true(isObject({}))
    t.true(isObject({ a: 1 }))
    t.true(isObject(new Date()))
    t.true(isObject(new RegExp('')))
    t.true(isObject([]))
    t.true(isObject([1, 2, 3]))
    
    // 正确识别非对象
    t.false(isObject(null))
    t.false(isObject(undefined))
    t.false(isObject('string'))
    t.false(isObject(123))
    t.false(isObject(true))
    t.false(isObject(Symbol('test')))
    t.false(isObject(BigInt(123)))
})

test('工具函数 - validateAndMerge 验证和合并选项', (t) => {
    // 正常合并
    const result1 = validateAndMerge({ a: 1 } as any, { b: 2 } as any)
    t.deepEqual(result1, { a: 1, b: 2 })
    
    // 处理 undefined
    const result2 = validateAndMerge({ a: 1 } as any, undefined, { b: 2 } as any)
    t.deepEqual(result2, { a: 1, b: 2 })
    
    // 空对象合并
    const result3 = validateAndMerge({}, {})
    t.deepEqual(result3, {})
    
    // 单个对象
    const result4 = validateAndMerge({ a: 1 } as any)
    t.deepEqual(result4, { a: 1 })
})

test('工具函数 - validateAndMerge 错误情况', (t) => {
    // 无效类型抛出错误
    t.throws(() => validateAndMerge('string' as any), {
        instanceOf: TypeError,
        message: 'The `options` argument must be an object'
    })
    
    t.throws(() => validateAndMerge(123 as any), {
        instanceOf: TypeError,
        message: 'The `options` argument must be an object'
    })
    
    t.throws(() => validateAndMerge([1, 2, 3] as any), {
        instanceOf: TypeError,
        message: 'The `options` argument must be an object'
    })
    
    t.throws(() => validateAndMerge(true as any), {
        instanceOf: TypeError,
        message: 'The `options` argument must be an object'
    })
})

test('工具函数 - mergeHeaders 合并请求头', (t) => {
    // 基本合并
    const headers1 = mergeHeaders({ 'Content-Type': 'application/json' }, { 'Authorization': 'Bearer token' })
    t.is(headers1.get('Content-Type'), 'application/json')
    t.is(headers1.get('Authorization'), 'Bearer token')
    
    // 覆盖相同键
    const headers2 = mergeHeaders({ 'X-Test': 'old' }, { 'X-Test': 'new' })
    t.is(headers2.get('X-Test'), 'new')
    
    // 处理 undefined 值 - 使用 Headers 实例来传递 undefined
    const source1 = new Headers({ 'X-Keep': 'keep', 'X-Remove': 'remove' })
    const source2 = new Headers()
    source2.set('X-Remove', 'undefined') // 这会被识别为字符串 'undefined' 并删除
    const headers3 = mergeHeaders(source1, source2)
    t.is(headers3.get('X-Keep'), 'keep')
    t.is(headers3.has('X-Remove'), false)
    
    // 空参数
    const headers4 = mergeHeaders()
    t.truthy(headers4 instanceof Headers)
    
    // Headers 实例作为输入
    const sourceA = new Headers({ 'Content-Type': 'text/plain' })
    const sourceB = new Headers({ 'Accept': 'application/json' })
    const headers5 = mergeHeaders(sourceA, sourceB)
    t.is(headers5.get('Content-Type'), 'text/plain')
    t.is(headers5.get('Accept'), 'application/json')
})

test('工具函数 - deepMerge 深度合并', (t) => {
    // 基本对象合并
    const result1 = deepMerge({ a: 1 } as any, { b: 2 } as any)
    t.deepEqual(result1, { a: 1, b: 2 })
    
    // 嵌套对象合并
    const result2 = deepMerge(
        { a: { x: 1, y: 2 } } as any, 
        { a: { y: 3, z: 4 }, b: 5 } as any
    )
    t.deepEqual(result2, { a: { x: 1, y: 3, z: 4 }, b: 5 })
    
    // 数组合并
    const result3 = deepMerge([1, 2] as any, [3, 4] as any)
    t.deepEqual(result3, [1, 2, 3, 4])
    
    // 对象到数组转换
    const result4 = deepMerge({} as any, [1, 2, 3] as any)
    t.deepEqual(result4, [1, 2, 3])
    
    // headers 特殊处理
    const result5 = deepMerge(
        { headers: { 'Content-Type': 'application/json' } } as any,
        { headers: { 'Authorization': 'Bearer token' } } as any
    )
    t.truthy((result5 as any).headers instanceof Headers)
    t.is(((result5 as any).headers as Headers).get('Content-Type'), 'application/json')
    t.is(((result5 as any).headers as Headers).get('Authorization'), 'Bearer token')

    // 处理 undefined 源
    const result6 = deepMerge({ a: 1 } as any, undefined, { b: 2 } as any)
    t.deepEqual(result6, { a: 1, b: 2 })
    
    // 复杂嵌套场景
    const result7 = deepMerge(
        { 
            config: { api: { timeout: 5000 } },
            headers: { 'X-Version': '1.0' }
        } as any,
        { 
            config: { api: { retries: 3 }, debug: true },
            headers: { 'X-Client': 'ceno' }
        } as any
    )
    t.deepEqual((result7 as any).config, { api: { timeout: 5000, retries: 3 }, debug: true })
    t.is(((result7 as any).headers as Headers).get('X-Version'), '1.0')
    t.is(((result7 as any).headers as Headers).get('X-Client'), 'ceno')
})

test('工具函数 - normalizeRequestMethod HTTP方法规范化', (t) => {
    // 标准方法转大写（小写输入）
    t.is(normalizeRequestMethod('get'), 'GET')
    t.is(normalizeRequestMethod('post'), 'POST')
    t.is(normalizeRequestMethod('put'), 'PUT')
    t.is(normalizeRequestMethod('delete'), 'DELETE')
    t.is(normalizeRequestMethod('patch'), 'PATCH')
    t.is(normalizeRequestMethod('head'), 'HEAD')
    
    // 已经是大写的保持不变
    t.is(normalizeRequestMethod('GET'), 'GET')
    t.is(normalizeRequestMethod('POST'), 'POST')
    
    // 非标准方法保持原样（包括混合大小写）
    t.is(normalizeRequestMethod('CUSTOM'), 'CUSTOM')
    t.is(normalizeRequestMethod('custom'), 'custom')
    t.is(normalizeRequestMethod('WebDAV'), 'WebDAV')
    t.is(normalizeRequestMethod('Get'), 'Get') // 'Get' 不在 requestMethods 中，所以保持原样
    t.is(normalizeRequestMethod('pAtCh'), 'pAtCh') // 'pAtCh' 不在 requestMethods 中，所以保持原样
})

test('工具函数 - normalizeRetryOptions 重试选项规范化', (t) => {
    // 数字形式
    const result1 = normalizeRetryOptions(3)
    t.is(result1.limit, 3)
    t.deepEqual(result1.methods, ['get', 'put', 'head', 'delete', 'options', 'trace'])
    t.deepEqual(result1.statusCodes, [408, 413, 429, 500, 502, 503, 504])
    t.deepEqual(result1.afterStatusCodes, [413, 429, 503])
    t.is(result1.maxRetryAfter, Number.POSITIVE_INFINITY)
    t.is(result1.backoffLimit, Number.POSITIVE_INFINITY)
    t.is(typeof result1.delay, 'function')
    
    // 对象形式 - 部分覆盖
    const result2 = normalizeRetryOptions({
        limit: 5,
        methods: ['get', 'post'],
        statusCodes: [500, 502]
    })
    t.is(result2.limit, 5)
    t.deepEqual(result2.methods, ['get', 'post'])
    t.deepEqual(result2.statusCodes, [500, 502])
    t.deepEqual(result2.afterStatusCodes, [413, 429, 503]) // 使用默认值
    
    // 空对象使用默认值
    const result3 = normalizeRetryOptions({})
    t.is(result3.limit, 2)
    t.deepEqual(result3.methods, ['get', 'put', 'head', 'delete', 'options', 'trace'])
    
    // undefined 使用默认值
    const result4 = normalizeRetryOptions()
    t.is(result4.limit, 2)
    
    // 完整自定义配置
    const customDelay = (attempt: number) => attempt * 1000
    const result5 = normalizeRetryOptions({
        limit: 10,
        methods: ['get'],
        statusCodes: [500],
        afterStatusCodes: [429],
        maxRetryAfter: 30000,
        backoffLimit: 5000,
        delay: customDelay
    })
    t.is(result5.limit, 10)
    t.deepEqual(result5.methods, ['get'])
    t.deepEqual(result5.statusCodes, [500])
    t.deepEqual(result5.afterStatusCodes, [429])
    t.is(result5.maxRetryAfter, 30000)
    t.is(result5.backoffLimit, 5000)
    t.is(result5.delay, customDelay)
    
    // 测试默认延迟函数
    const defaultDelay = normalizeRetryOptions().delay
    t.is(defaultDelay(1), 300) // 0.3 * (2^0) * 1000 = 300
    t.is(defaultDelay(2), 600) // 0.3 * (2^1) * 1000 = 600
    t.is(defaultDelay(3), 1200) // 0.3 * (2^2) * 1000 = 1200
})

test('工具函数 - normalizeRetryOptions 错误情况', (t) => {
    // methods 不是数组
    t.throws(() => normalizeRetryOptions({ methods: 'get' as any }), {
        instanceOf: Error,
        message: 'retry.methods must be an array'
    })
    
    // statusCodes 不是数组
    t.throws(() => normalizeRetryOptions({ statusCodes: 500 as any }), {
        instanceOf: Error,
        message: 'retry.statusCodes must be an array'
    })
})

test('工具函数 - findUnknownOptions 查找未知选项', (t) => {
    // 模拟 Request 对象
    const mockRequest = {
        method: 'GET',
        url: 'http://example.com',
        headers: new Headers()
    } as Request
    
    // 没有未知选项
    const result1 = findUnknownOptions(mockRequest, {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        json: { test: 'data' }, // ceno 选项
        retry: { limit: 3 } // ceno 选项
    })
    t.deepEqual(result1, {})
    
    // 有未知选项
    const result2 = findUnknownOptions(mockRequest, {
        method: 'POST', // Request 选项
        json: { test: 'data' }, // ceno 选项
        customOption: 'custom value', // 未知选项
        anotherUnknown: 123 // 未知选项
    })
    t.deepEqual(result2, {
        customOption: 'custom value',
        anotherUnknown: 123
    })
    
    // Request 自有属性不算未知
    const result3 = findUnknownOptions(mockRequest, {
        url: 'http://test.com', // Request 属性
        unknownProp: 'value'
    })
    t.deepEqual(result3, { unknownProp: 'value' })
    
    // 空选项
    const result4 = findUnknownOptions(mockRequest, {})
    t.deepEqual(result4, {})
})

test('工具函数 - delay 基本延迟', async (t) => {
    const start = Date.now()
    await delay(100, {})
    const elapsed = Date.now() - start
    
    t.true(elapsed >= 100)
    t.true(elapsed < 200) // 允许一些误差
})

test('工具函数 - delay 取消信号', async (t) => {
    const controller = new AbortController()
    
    // 立即取消
    controller.abort(new Error('Test abort'))
    
    await t.throwsAsync(
        () => delay(1000, { signal: controller.signal }),
        { message: 'Test abort' }
    )
})

test('工具函数 - delay 延迟后取消', async (t) => {
    const controller = new AbortController()
    
    // 50ms 后取消
    setTimeout(() => controller.abort(new Error('Delayed abort')), 50)
    
    await t.throwsAsync(
        () => delay(1000, { signal: controller.signal }),
        { message: 'Delayed abort' }
    )
})

test('工具函数 - delay 没有信号', async (t) => {
    const start = Date.now()
    await delay(50, {})
    const elapsed = Date.now() - start
    
    t.true(elapsed >= 50)
})

test('工具函数 - delay 零延迟', async (t) => {
    const start = Date.now()
    await delay(0, {})
    const elapsed = Date.now() - start
    
    t.true(elapsed < 50) // 应该几乎立即完成
})

test('工具函数 - delay 已取消的信号', async (t) => {
    const controller = new AbortController()
    controller.abort(new Error('Already aborted'))
    
    await t.throwsAsync(
        () => delay(100, { signal: controller.signal }),
        { message: 'Already aborted' }
    )
})

test('工具函数 - 复杂的 deepMerge 场景', (t) => {
    // 混合数组和对象
    const result1 = deepMerge(
        { items: [1, 2] } as any,
        { items: [3, 4], config: { debug: true } } as any
    )
    t.deepEqual(result1, {
        items: [1, 2, 3, 4],
        config: { debug: true }
    })
    
    // 多层嵌套
    const result2 = deepMerge(
        { a: { b: { c: 1 } } } as any,
        { a: { b: { d: 2 } } } as any,
        { a: { e: 3 } } as any
    )
    t.deepEqual(result2, {
        a: { b: { c: 1, d: 2 }, e: 3 }
    })
    
    // null 和 undefined 处理
    const result3 = deepMerge(
        { a: null, b: undefined } as any,
        { a: 'value', c: 'new' } as any
    )
    t.deepEqual(result3, { a: 'value', b: undefined, c: 'new' })
})

test('工具函数 - validateAndMerge 与 deepMerge 集成', (t) => {
    const result = validateAndMerge(
        { a: { x: 1 } } as any,
        { a: { y: 2 }, b: 3 } as any,
        undefined,
        { c: 4 } as any
    )
    
    t.deepEqual(result, {
        a: { x: 1, y: 2 },
        b: 3,
        c: 4
    })
})